﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Spanner
{
    public partial class UI
    {
        // Every HTML element has a corresponding Spanner function.

        public static Html A(params Html[] children) { return new Html { Tag = "a", Children = children }; }
        public static Html Abbr(params Html[] children) { return new Html { Tag = "abbr", Children = children }; }
        public static Html Address(params Html[] children) { return new Html { Tag = "address", Children = children }; }
        public static Html Area() { return new Html { Tag = "area" }; }
        public static Html Article(params Html[] children) { return new Html { Tag = "article", Children = children }; }
        public static Html Aside(params Html[] children) { return new Html { Tag = "aside", Children = children }; }
        public static Html Audio(params Html[] children) { return new Html { Tag = "audio", Children = children }; }
        public static Html B(params Html[] children) { return new Html { Tag = "b", Children = children }; }
        public static Html Base() { return new Html { Tag = "base" }; }
        public static Html Bdi(params Html[] children) { return new Html { Tag = "bdi", Children = children }; }
        public static Html Bdo(params Html[] children) { return new Html { Tag = "bdo", Children = children }; }
        public static Html BlockQuote(params Html[] children) { return new Html { Tag = "blockquote", Children = children }; }
        /// <remarks>The <see cref="StandardSpannerWebPage"/> method supplies this boilerplate automatically.</remarks>
        public static Html Body(params Html[] children) { return new Html { Tag = "body", Children = children }; }
        public static Html BR() { return new Html { Tag = "br" }; }
        public static Html Button(params Html[] children) { return new Html { Tag = "button", Children = children }; }
        public static Html Canvas(params Html[] children) { return new Html { Tag = "canvas", Children = children }; }
        public static Html Caption(params Html[] children) { return new Html { Tag = "caption", Children = children }; }
        public static Html CDATA(params string[] data) { return new CDataHtml { Data = data }; }
        public static Html Cite(params Html[] children) { return new Html { Tag = "cite", Children = children }; }
        public static Html Code(params Html[] children) { return new Html { Tag = "code", Children = children }; }
        public static Html Col() { return new Html { Tag = "col" }; }
        public static Html ColGroup(params Html[] children) { return new Html { Tag = "colgroup", Children = children }; }
        public static Html Command() { return new Html { Tag = "command" }; }
        public static Html Data(params Html[] children) { return new Html { Tag = "data", Children = children }; }
        public static Html DataGrid(params Html[] children) { return new Html { Tag = "datagrid", Children = children }; }
        public static Html DataList(params Html[] children) { return new Html { Tag = "datalist", Children = children }; }
        public static Html DD(params Html[] children) { return new Html { Tag = "dd", Children = children }; }
        public static Html Del(params Html[] children) { return new Html { Tag = "del", Children = children }; }
        public static Html Details(params Html[] children) { return new Html { Tag = "details", Children = children }; }
        /// <summary>
        /// Add a !DOCTYPE prefix to an HTML page.
        /// </summary>
        /// <param name="document"></param>
        /// <returns></returns>
        /// <remarks>The <see cref="StandardSpannerWebPage"/> method supplies this boilerplate automatically.</remarks>
        public static Html Doctype(Html document) { return new DoctypeHtml { Document = document }; }
        public static Html Dfn(params Html[] children) { return new Html { Tag = "dfn", Children = children }; }
        public static Html Div(params Html[] children) { return new Html { Tag = "div", Children = children }; }
        public static Html DL(params Html[] children) { return new Html { Tag = "dl", Children = children }; }
        public static Html DT(params Html[] children) { return new Html { Tag = "dt", Children = children }; }
        public static Html Em(params Html[] children) { return new Html { Tag = "em", Children = children }; }
        public static Html Embed() { return new Html { Tag = "embed" }; }
        public static Html EventSource(params Html[] children) { return new Html { Tag = "eventsource", Children = children }; }
        public static Html FieldSet(params Html[] children) { return new Html { Tag = "fieldset", Children = children }; }
        public static Html FigCaption(params Html[] children) { return new Html { Tag = "figcaption", Children = children }; }
        public static Html Figure(params Html[] children) { return new Html { Tag = "figure", Children = children }; }
        public static Html Footer(params Html[] children) { return new Html { Tag = "footer", Children = children }; }
        public static Html Form(params Html[] children) { return new Html { Tag = "form", Children = children }; }
        public static Html H1(params Html[] children) { return new Html { Tag = "h1", Children = children }; }
        public static Html H2(params Html[] children) { return new Html { Tag = "h2", Children = children }; }
        public static Html H3(params Html[] children) { return new Html { Tag = "h3", Children = children }; }
        public static Html H4(params Html[] children) { return new Html { Tag = "h4", Children = children }; }
        public static Html H5(params Html[] children) { return new Html { Tag = "h5", Children = children }; }
        public static Html H6(params Html[] children) { return new Html { Tag = "h6", Children = children }; }
        /// <remarks>The <see cref="StandardSpannerWebPage"/> method supplies this boilerplate automatically.</remarks>
        public static Html Head(params Html[] children) { return new Html { Tag = "head", Children = children }; }
        public static Html Header(params Html[] children) { return new Html { Tag = "header", Children = children }; }
        public static Html HGroup(params Html[] children) { return new Html { Tag = "hgroup", Children = children }; }
        public static Html HR() { return new Html { Tag = "hr" }; }
        public static Html Html(params Html[] children) { return new Html { Tag = "html", Children = children }; }
        public static Html I(params Html[] children) { return new Html { Tag = "i", Children = children }; }
        public static Html IFrame(params Html[] children) { return new Html { Tag = "iframe", Children = children }; }
        public static Html Img() { return new Html { Tag = "img" }; }
        public static Html Input() { return new Html { Tag = "input" }; }
        public static Html Ins(params Html[] children) { return new Html { Tag = "ins", Children = children }; }
        public static Html Kbd(params Html[] children) { return new Html { Tag = "kbd", Children = children }; }
        public static Html KeyGen() { return new Html { Tag = "keygen" }; }
        public static Html Label(params Html[] children) { return new Html { Tag = "label", Children = children }; }
        public static Html Legend(params Html[] children) { return new Html { Tag = "legend", Children = children }; }
        public static Html LI(params Html[] children) { return new Html { Tag = "li", Children = children }; }
        public static Html Link(params Html[] children) { return new Html { Tag = "link", Children = children }; }
        public static Html Mark(params Html[] children) { return new Html { Tag = "mark", Children = children }; }
        public static Html ImageMap(params Html[] children) { return new Html { Tag = "map", Children = children }; }
        public static Html Menu(params Html[] children) { return new Html { Tag = "menu", Children = children }; }
        public static Html Meta() { return new Html { Tag = "meta" }; }
        public static Html Meter(params Html[] children) { return new Html { Tag = "meter", Children = children }; }
        public static Html Nav(params Html[] children) { return new Html { Tag = "nav", Children = children }; }
        public static Html Noscript(params Html[] children) { return new Html { Tag = "noscript", Children = children }; }
        public static Html Object(params Html[] children) { return new Html { Tag = "object", Children = children }; }
        public static Html OL(params Html[] children) { return new Html { Tag = "ol", Children = children }; }
        public static Html OptGroup(params Html[] children) { return new Html { Tag = "optgroup", Children = children }; }
        public static Html Option(params Html[] children) { return new Html { Tag = "option", Children = children }; }
        public static Html Output(params Html[] children) { return new Html { Tag = "output", Children = children }; }
        public static Html P(params Html[] children) { return new Html { Tag = "p", Children = children }; }
        public static Html Param() { return new Html { Tag = "param" }; }
        public static Html Pre(params Html[] children) { return new Html { Tag = "pre", Children = children }; }
        public static Html Progress(params Html[] children) { return new Html { Tag = "progress", Children = children }; }
        public static Html Q(params Html[] children) { return new Html { Tag = "q", Children = children }; }
        public static Html Ruby(params Html[] children) { return new Html { Tag = "ruby", Children = children }; }
        public static Html RP(params Html[] children) { return new Html { Tag = "rp", Children = children }; }
        public static Html RT(params Html[] children) { return new Html { Tag = "rt", Children = children }; }
        public static Html S(params Html[] children) { return new Html { Tag = "s", Children = children }; }
        public static Html Samp(params Html[] children) { return new Html { Tag = "samp", Children = children }; }
        public static Html Script(params Html[] children) { return new Html { Tag = "script", Children = children }; }
        public static Html Section(params Html[] children) { return new Html { Tag = "section", Children = children }; }
        public static Html Select(params Html[] children) { return new Html { Tag = "select", Children = children }; }
        public static Html Small(params Html[] children) { return new Html { Tag = "small", Children = children }; }
        public static Html Source() { return new Html { Tag = "source" }; }
        public static Html Span(params Html[] children) { return new Html { Tag = "span", Children = children }; }
        public static Html Strong(params Html[] children) { return new Html { Tag = "strong", Children = children }; }
        public static Html Style(params Html[] children) { return new Html { Tag = "style", Children = children }; }
        public static Html Sub(params Html[] children) { return new Html { Tag = "sub", Children = children }; }
        public static Html Summary(params Html[] children) { return new Html { Tag = "summary", Children = children }; }
        public static Html Sup(params Html[] children) { return new Html { Tag = "sup", Children = children }; }
        public static Html Table(params Html[] children) { return new Html { Tag = "table", Children = children }; }
        public static Html TBody(params Html[] children) { return new Html { Tag = "tbody", Children = children }; }
        public static Html TD(params Html[] children) { return new Html { Tag = "td", Children = children }; }
        public static Html TextArea(params Html[] children) { return new Html { Tag = "textarea", Children = children }; }
        public static Html TFoot(params Html[] children) { return new Html { Tag = "tfoot", Children = children }; }
        public static Html TH(params Html[] children) { return new Html { Tag = "th", Children = children }; }
        public static Html THead(params Html[] children) { return new Html { Tag = "thead", Children = children }; }
        public static Html Time(params Html[] children) { return new Html { Tag = "time", Children = children }; }
        public static Html Title(params Html[] children) { return new Html { Tag = "title", Children = children }; }
        public static Html TR(params Html[] children) { return new Html { Tag = "tr", Children = children }; }
        public static Html Track() { return new Html { Tag = "track" }; }
        public static Html U(params Html[] children) { return new Html { Tag = "u", Children = children }; }
        public static Html UL(params Html[] children) { return new Html { Tag = "ul", Children = children }; }
        public static Html VAR(params Html[] children) { return new Html { Tag = "var", Children = children }; }
        public static Html Video(params Html[] children) { return new Html { Tag = "video", Children = children }; }
        public static Html Wbr() { return new Html { Tag = "wbr" }; }

        public static HtmlID HtmlID(string id) {
            return new HtmlID { Name = id };
        }

        /// <summary>
        /// An HTML comment.
        /// </summary>
        /// <param name="text">This appears verbatim in the HTML comment.</param>
        /// <returns></returns>
        public static Html HtmlComment(string text) { return new HtmlCommentClass { Text = text }; }

        internal class HtmlCommentClass : Html
        {
            internal string Text;

            internal override void WriteHtml(Context ctxt)
            {
                ctxt.WriteLine("<!-- {0} -->", Text);
            }
        }

        internal class DoctypeHtml : Html
        {
            internal Html Document;

            internal override void WriteHtml(Context ctxt)
            {
                ctxt.WriteLine("<!DOCTYPE html>");
                ctxt.WriteHtml(Document);
            }
        }

        internal class CDataHtml : Html
        {
            internal string[] Data;

            internal override void WriteHtml(Context ctxt)
            {
                if (Data == null) return;
                ctxt.WritePlain("<![CDATA[");
                for (var i = 0; i < Data.Length - 1; i++) ctxt.WritePlainLine(Data[i]);
                ctxt.WritePlain(Data[Data.Length - 1]);
                ctxt.WritePlain("]]>");
            }
        }

        /// <summary>
        /// This allows us to treat a sequence of HTML elements as a single element, without
        /// adding anything to the generated markup.
        /// </summary>
        /// <remarks>
        /// You cannot add attributes or data-bind to a group element.
        /// </remarks>
        public static Html Group(params Html[] children) { return new HtmlGroup { Children = children }; }

        public static Html Group(IEnumerable<Html> children) { return Group(children.ToArray()); }

        internal class HtmlGroup : Html
        {
            internal override void WriteHtml(Context ctxt)
            {
                if (Children != null) foreach (var child in Children) child.WriteHtml(ctxt);
            }

            public override Html WithAttr(string key, object value)
            {
                throw new ApplicationException("Attributes cannot be attached to an HtmlGroup, only to its children.");
            }

            public override Html DataBind<T>(string key, Expr<T> value)
            {
                throw new ApplicationException("Data bindings cannot be attached to an HtmlGroup, only to its children.");
            }
        }

        // Extensions to show expression values inline.

        /// <summary>
        /// Show a string expression in an HTML span.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Html Show(Expr<string> e) { return Span().KoText(e); }
        /// <summary>
        /// Show an int expression in an HTML span.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Html Show(Expr<int> e) { return Span().KoText(e); }
        /// <summary>
        /// Show a double expression in an HTML span.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public static Html Show(Expr<double> e) { return Span().KoText(e); }

        // Write out the JavaScript for the view model.

        /// <summary>
        /// Write out the JavaScript references and JavaScript code for the web page
        /// view model.  This automatically handles dependency ordering, with the
        /// caveat that no order is guaranteed on cyclic dependencies.
        /// 
        /// The emitted code binds the web page view model via Knockouts ko.applyBinding
        /// when the web page has completely loaded.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// It is recommended this be the last thing in the body of your web page.
        /// <see cref="StandardSpannerHtmlPage"/> automatically supplies this for you.
        /// </remarks>
        public static Html ViewModelJavaScript()
        {
            return new ViewModelJavaScriptHtml();
        }

        internal class ViewModelJavaScriptHtml : Html
        {
            internal override void WriteHtml(Context ctxt)
            {
                ctxt.WriteViewModelJSScript();
            }
        }

        // Almost all HTML pages we generate will look like this; this just saves boilerplate.

        /// <summary>
        /// This abstracts out the typical HTML boiler-plate in setting up a web page.
        /// 
        /// StandardSpannerHtmlPage(title, headerContent, bodyContent) is equivalent to
        /// 
        /// Doctype(Html(Head(Title(title), headerContent), Body(bodyContent, ViewModelJavaScript())))
        /// </summary>
        /// <param name="title"></param>
        /// <param name="headerContent">This goes in the web page head section.</param>
        /// <param name="bodyContent">This goes in the web page body section.</param>
        /// <returns></returns>
        public static Html StandardSpannerHtmlPage(string title, Html headerContent, Html bodyContent)
        {
            return Doctype(
                Html(
                    Head(
                        Title(title),
                        headerContent
                    ),
                    Body(
                        bodyContent,
                        ViewModelJavaScript()
                    )
                )
            );
        }

        // Almost all HTML pages we generate will look like this; this just saves boilerplate.

        /// <summary>
        /// This abstracts out the typical HTML boiler-plate in setting up a web page.
        /// 
        /// StandardSpannerHtmlPage(title, bodyContent) is equivalent to
        /// 
        /// Doctype(Html(Head(Title(title)), Body(bodyContent, ViewModelJavaScript())))
        /// </summary>
        /// <param name="title"></param>
        /// <param name="bodyContent">This goes in the web page body section.</param>
        /// <returns></returns>
        public static Html StandardSpannerHtmlPage(string title, Html bodyContent)
        {
            return Doctype(
                Html(
                    Head(
                        Title(title)
                    ),
                    Body(
                        bodyContent,
                        ViewModelJavaScript()
                    )
                )
            );
        }
    }
}
